package {
	/**
	 * visual. for In the Rowans by Balmorhea
	 * 2014-5-22, copyleft.
	 * @author fallensky
	 */
	//import flash.filters.BlurFilter;
	//import flash.filters.GlowFilter;
	
	import flash.text.TextFormat;
	import flash.text.TextField;
	
	import flash.ui.Mouse;
	
	import flash.display.LoaderInfo;
	import flash.display.Sprite;

	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.MouseEvent;

	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	
	import flash.media.Sound;
	import flash.media.SoundChannel;
	
	import com.fallensky.CustomStatus;
	import com.fallensky.Math2;

	import com.greensock.TweenLite;
	import com.greensock.easing.*;

	
	[SWF(width=640, height=560, backgroundColor=0xffffff, frameRate=60)]
	
	public class Main extends Sprite {
		[Embed(source='../lib/Oswald-Light.ttf', fontName="Oswald", mimeType="application/x-font-truetype", embedAsCFF="false")]
		private var font:Class;
		
		// some major global const.s
		private const DOC_X:int=640;
		private const DOC_Y:int=560;
		
		private const CHAR_FONT_NAME:String="Oswald";
		private const CHAR_FONT_COLOR:int=0;
		private const CHAR_FONT_SIZE:Number=16;
		
		private const CHAR_X:int=DOC_X-40;
		private const CHAR_LINESPACE:Number=22;
		
		private const CHAR_ALPHA_START:Number=0.375;
		private const CHAR_ALPHA_END:Number=0.55;
		private const CHAR_UP:int=-30;
		private const CHAR_BOTTOM:int=DOC_Y+30;
		
		private const CENTER_Y:int=DOC_Y/3;
		
		
		// some major global var.s
		private var status:CustomStatus;
		
		private var mp3Filename:String;
		private var mp3:Sound;
		private var mp3Channel:SoundChannel;
		private var mp3Position:Number;
		private var mp3Completed:Boolean;
		private var mp3Loaded:Boolean;
		
		private var txtFilename:String;
		private var txt:URLLoader;
		private var txtLoaded:Boolean;
		
		private var char:Vector.<CustomTextField>;
		private var credit:Vector.<CustomTextField>;
		
		private var charStage:Sprite;
		private var creditStage:Sprite;
		
		private var phase:Vector.<Number>;
		
		private var fallingP:Number;
		
		private var fallenChar:uint;
		private var typedChar:uint;
		private var collapsedChar:uint;
		private var creditChar:uint;
		
		private var fallenCharX:Number;
		private var fallenCharY:Number;
		
		private var curPhase:int;
		
		
		// for convenience
		private var i:int;
		private var t:String;
		private var tmpURL:String;	// due to security restriction
		private var tCTF:CustomTextField;
		
		
		public function Main() {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			

			// status
			status=new CustomStatus(5,5,0x0000ff,320);
			addChild(status);
						
			
			phase=new Vector.<Number>;
			phase.push(0);
			phase.push(16.25);	// starts falling letters (1)
			phase.push(57);		// slowly starts typing (2)
			phase.push(60+45);	// typed letters vanishing & rising letters (3)
			phase.push(120+8.5);	// starts typing fast! (4)
			phase.push(120+38);	// collapsing letters (5)
			phase.push(180+14);	// starts falling letters (6)
			phase.push(180+43);	// credit typing (7)
			phase.push(240+3);	// still falling letters (8)
			phase.push(Infinity);
			for(i=9;i--;)
				phase[i]*=1000;
			
			// txt
			txtFilename=loadFlashVars(["txt"])[0];
			if(txtFilename==null) txtFilename="The Music of Erich Zann.txt";
			txtLoaded=false;
			loadTxt();
			
			// mp3
			mp3Filename=loadFlashVars(["mp3"])[0];
			if(mp3Filename==null) mp3Filename="balmorhea-in-the-rowans.mp3";
			mp3Loaded=false;
			loadMp3();
			
			addEventListener(Event.ENTER_FRAME, preload);
		}
		
		private function preload(e:Event):void {
			if(txtLoaded && mp3Loaded) {
				removeEventListener(Event.ENTER_FRAME, preload);
				begin();
			}
		}
		
		
		// txt loading
		private function loadTxt():void {
			tmpURL=txtFilename;
			
			txt=new URLLoader();
			txt.addEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			txt.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			txt.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			txt.addEventListener(Event.COMPLETE, loadTxtCompleteHandler);

			var request:URLRequest=new URLRequest(txtFilename);
			request.method=URLRequestMethod.POST;
			txt.load(request);
		}

		private function loadTxtCompleteHandler(e:Event):void {
			const CRLF_REG:RegExp=new RegExp("\r\n","g");
			const LF_REG:RegExp=new RegExp("\n","g");
			
			txt.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			txt.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			txt.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
			txt.removeEventListener(Event.COMPLETE, loadTxtCompleteHandler);
			
			t=String(txt.data).replace(CRLF_REG,"\r").replace(LF_REG,"\r");	// CR+LF -> CR only
			char=new Vector.<CustomTextField>();
			makeChars(char, t, CHAR_FONT_SIZE);
			
			charStage=new Sprite();
			addChild(charStage);
			
			status.flush("txt loading completed");
			txtLoaded=true;
		}
		
		private function makeChars(tv:Vector.<CustomTextField>, t:String, fontSize:Number):void {
			var tTextFormat:TextFormat=new TextFormat(CHAR_FONT_NAME, fontSize, CHAR_FONT_COLOR);
			
			for(i=0;i<t.length;i++) {
				tCTF=new CustomTextField();
				with(tCTF) {
					autoSize="left";
					embedFonts=true;
					selectable=false;
					defaultTextFormat=tTextFormat;
					text=t.charAt(i);
					antiAliasType="advanced";
				}
				tv.push(tCTF);
			}
		}

		
		// mp3 loading
		private function loadMp3():void {
			tmpURL=mp3Filename;
			
			mp3=new Sound();
			mp3.addEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			mp3.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			mp3.addEventListener(ProgressEvent.PROGRESS, progressHandler);
			mp3.addEventListener(Event.COMPLETE, loadMp3CompleteHandler);
			mp3.load(new URLRequest(mp3Filename));
		}
		
		private function loadMp3CompleteHandler(e:Event):void {
			mp3.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			mp3.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			mp3.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
			mp3.removeEventListener(Event.COMPLETE, loadMp3CompleteHandler);
			
			makeAndAddCredit();
			
			status.flush("mp3 loading completed.");
			mp3Loaded=true;
		}

		
		private function makeAndAddCredit():void {
			var tx:Number, ty:Number;
			var tTextFormat:TextFormat=new TextFormat(CHAR_FONT_NAME, 20, CHAR_FONT_COLOR);
			
			t=mp3.id3.artist+" - "+mp3.id3.songName+" ("+mp3.id3.year+")";
			
			tCTF=new CustomTextField();
			with(tCTF) {
				autoSize="left";
				embedFonts=true;
				defaultTextFormat=tTextFormat;
				text=t;
				antiAliasType="advanced";
			}
			tx=(DOC_X-tCTF.textWidth)/2;
			ty=(DOC_Y-tCTF.textHeight)/2;
			
			credit=new Vector.<CustomTextField>;
			creditStage=new Sprite();
			addChild(creditStage);
			
			makeChars(credit, t, 20);
			
			for(i=0;i<t.length;i++) {
				tCTF=credit[i];
				with(tCTF) {
					orgX=tx;
					orgY=ty;
					x=Math2.random(0, DOC_X);
					y=Math2.random(-75, -50);
				}
				creditStage.addChild(tCTF);
				tx+=tCTF.textWidth;
			}
		}

		
		private function begin():void {
			mp3Channel=mp3.play();	// ***
			mp3Channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
			
			fallingP=1/1000;
			
			fallenChar=0;
			typedChar=0;
			creditChar=0;
			
			fallenCharX=0;
			fallenCharY=CHAR_LINESPACE;
			curPhase=0;
			
			charStage.x=10;
			charStage.y=CHAR_LINESPACE;
			
			creditStage.alpha=1;
			
			TweenLite.to(status, 0.5, {alpha:0, ease:Linear.easeNone});
			
			addEventListener(Event.ENTER_FRAME, idle);
		}
		
		
		private function idle(e:Event):void {
			if(mp3Channel.position>=phase[curPhase+1]) curPhase++;
			
					
			switch(curPhase) {
				case 1:	// starts falling letters (1)
				case 6:	// starts falling letters (6)
					tCTF=char[fallenChar];
					
					if(fallingP>Math.random()) {
						with(tCTF) {
							orgX=fallenCharX;
							orgY=fallenCharY;
							x=Math2.random(0, DOC_X);
							y=Math2.random(-50, -25);
							alpha=CHAR_ALPHA_START;
						}
						addChild(tCTF);
						
						if(tCTF.text!=" " && tCTF.text!="\r")
							TweenLite.to(tCTF, Math2.random(1.25,3.5), {y:CHAR_BOTTOM, alpha:CHAR_ALPHA_END, rotation:Math2.random(-180,180), ease:Sine.easeIn});
						
						fallenCharX+=tCTF.textWidth;
						if(fallenCharX>CHAR_X+5 || tCTF.text=="\r") {
							fallenCharX=0;
							fallenCharY+=CHAR_LINESPACE;
						}
						
						if(++fallenChar>=char.length) fallenChar=0;
					}
					else {
						fallingP*=1.0075;
					}
					
					break;
				case 2:	// slowly starts typing (2)
					tCTF=char[typedChar];
					
					removeChild(tCTF);
					
					if(tCTF.orgX==0 && tCTF.orgY>CENTER_Y)
						charStage.y-=CHAR_LINESPACE;
					
					charStage.addChild(tCTF);
					tCTF.y=-charStage.y+CHAR_BOTTOM;
					tCTF.scaleX=tCTF.scaleY=1.775;
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, 1.775, {x:tCTF.orgX, y:tCTF.orgY, scaleX:1, scaleY:1, alpha:1, rotation:0, ease:Circ.easeInOut});
						
					typedChar++;
					
					
					// falling letters
					tCTF=char[fallenChar];
		
					with(tCTF) {
						orgX=fallenCharX;
						orgY=fallenCharY;
						x=Math2.random(0, DOC_X);
						y=Math2.random(-50, -25);
						alpha=CHAR_ALPHA_START;
					}
					addChild(tCTF);
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, Math2.random(1.25,3.5), {y:CHAR_BOTTOM, alpha:CHAR_ALPHA_END, rotation:Math2.random(-180,180), ease:Sine.easeIn});
					
					fallenCharX+=tCTF.textWidth;
					if(fallenCharX>CHAR_X+5 || tCTF.text=="\r") {
						fallenCharX=0;
						fallenCharY+=CHAR_LINESPACE;
					}
					
					if(++fallenChar>=char.length) fallenChar=0;
					
					break;
				case 3:	// typed letters vanishing & rising letters (3)
					if(charStage.alpha>0.1)
						charStage.alpha*=0.925;	// after 30frames(=0.5sec) alpha becomes smaller than 0.1
					else
						charStage.visible=false;
					
					tCTF=char[fallenChar];
				
					with(tCTF) {
						orgX=fallenCharX;
						orgY=fallenCharY;
						x=Math2.random(0, DOC_X);
						y=Math2.random(DOC_Y+25, DOC_Y+50);
						alpha=CHAR_ALPHA_START;
					}
					addChild(tCTF);
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, Math2.random(1,3.25), {y:CHAR_UP, alpha:CHAR_ALPHA_END, rotation:Math2.random(-180,180), ease:Sine.easeIn});
					
					fallenCharX+=tCTF.textWidth;
					if(fallenCharX>CHAR_X+5 || tCTF.text=="\r") {
						fallenCharX=0;
						fallenCharY+=CHAR_LINESPACE;
					}
					
					if(++fallenChar>=char.length) fallenChar=0;

					break;
				case 4:	// starts typing fast! (4)
					if(!charStage.visible) {
						charStage.visible=true;
						TweenLite.to(charStage, 0.5, {alpha:1, ease:Linear.easeNone});
					}

					tCTF=char[typedChar];
					
					removeChild(tCTF);
					
					if(tCTF.orgX==0 && tCTF.orgY>CENTER_Y)
						charStage.y-=CHAR_LINESPACE;
					
					charStage.addChild(tCTF);
					if(Math2.coin2())
						tCTF.x=Math2.random(-25,-15);
					else
						tCTF.x=Math2.random(DOC_X+15,DOC_X+25);
					
					if(Math2.coin2())
						tCTF.y=Math2.random(-25,-15);
					else
						tCTF.y=Math2.random(DOC_Y+15,DOC_Y+25);
					
					tCTF.scaleX=tCTF.scaleY=40;
					tCTF.rotation=Math2.random(-90,90);
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, 0.3, {x:tCTF.orgX, y:tCTF.orgY, scaleX:1, scaleY:1, alpha:1, rotation:0, ease:Circ.easeInOut});
						
					collapsedChar=typedChar;	// for the next phase
					
					typedChar++;				// no array index check -_-
					
					break;
				case 5: // collapsing letters (5)
					tCTF=char[collapsedChar];
		
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, Math2.random(1.25,2.75), {y:-charStage.y+CHAR_BOTTOM, ease:Sine.easeIn});
		
					collapsedChar--;			// no array index check -_-
					
					fallingP=1/1000;			// for the next phase
					
					break;
				case 7: // credit typing (7)
					tCTF=credit[creditChar];
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, 3.5, {x:tCTF.orgX, y:tCTF.orgY, alpha:1, ease:Back.easeOut});
						
					if(++creditChar>=credit.length)
						curPhase++;
					
					// continues
				case 8:	
					// falling letters (same as (2))
					tCTF=char[fallenChar];
		
					with(tCTF) {
						orgX=fallenCharX;
						orgY=fallenCharY;
						x=Math2.random(0, DOC_X);
						y=Math2.random(-50, -25);
						alpha=CHAR_ALPHA_START;
					}
					addChild(tCTF);
					
					if(tCTF.text!=" " && tCTF.text!="\r")
						TweenLite.to(tCTF, Math2.random(1.25,3.5), {y:CHAR_BOTTOM, alpha:CHAR_ALPHA_END, rotation:Math2.random(-180,180), ease:Sine.easeIn});
					
					fallenCharX+=tCTF.textWidth;
					if(fallenCharX>CHAR_X+5 || tCTF.text=="\r") {
						fallenCharX=0;
						fallenCharY+=CHAR_LINESPACE;
					}
					
					if(++fallenChar>=char.length) fallenChar=0;
					
					break;
				case 0:
					// nothing to do
			}
		}
		
		
		private function soundCompleteHandler(e:Event):void {
			mp3Channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
			
			removeEventListener(Event.ENTER_FRAME, idle);
			addEventListener(MouseEvent.CLICK, clickHandler);
			
			status.flush("click to play again.");
			
			TweenLite.to(creditStage, 0.5, {alpha:0, ease:Linear.easeNone});
		}
		

		private function clickHandler(e:MouseEvent):void {
			removeEventListener(MouseEvent.CLICK, clickHandler);
			
			// hides typed letters
			for(i=typedChar;i--;)
				char[i].y=CHAR_BOTTOM;

			// hides credit
			for(i=0;i<credit.length;i++) {	
				tCTF=credit[i];
				with(tCTF) {
					x=Math2.random(0, DOC_X);
					y=Math2.random(-75, -50);
				}
			}
			
			begin();
		}
		
		
		// common flash var loading
		private function loadFlashVars(fvNames:Array):Array {
			var paramObj:Object=LoaderInfo(root.loaderInfo).parameters;
			var result:Array=[];
			
			for(t in paramObj) {
				for(var i:int=0;i<fvNames.length;i++) {
					if(t==fvNames[i] && String(paramObj[t])!=null)
						result.push(String(paramObj[t]));
				}
			}
			
			return(result);
		}
		
		
		// common handlers
		private function IOErrorHandler(e:IOErrorEvent):void {
			status.flush("I/O error occured when loading "+tmpURL+". ");
		}
		
		private function securityErrorHandler(e:SecurityErrorEvent):void {
			status.flush("security error occured when loading "+tmpURL+". ");
		}

		private function progressHandler(e:ProgressEvent):void {
			status.flush("loading "+tmpURL+": "+e.currentTarget.bytesLoaded+"/"+e.currentTarget.bytesTotal+"...");
		}

	}
}


import flash.text.TextField;

class CustomTextField extends TextField {
	public var orgX:Number;
	public var orgY:Number;
	
	public function CustomTextField():void {
		super();
	}
}